package org.sixtynine.stock.util;

import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.sixtynine.stock.entity.IntradaySectorData;
import org.sixtynine.stock.entity.IntradayShareData;
import org.sixtynine.stock.result.Result;
import org.sixtynine.stock.result.Sortable;

/**
 * helping comparators
 * 
 * @author Thilanka
 * 
 */
public class StockComparator {

	private static StockComparator comparator;
	private static Map<Integer, Comparator<?>> comparatorMap = new HashMap<Integer, Comparator<?>>();

	private static Logger logger = Logger.getLogger(StockComparator.class);

	private StockComparator() {

	}

	public static final int INTRADAY_TIME_CMP = 0;
	public static final int INTRADAY_SECTOR_TIME_CMP = 1;
	public static final int TOP_GAINER = 2;
	public static final int TOP_LOOSER = 3;
	public static final int HIGHEST_TURNOVER = 4;
	public static final int MOST_ACTIVE = 5;
	public static final int LARGE_CAP = 6;
	public static final int MOST_VOLATALITY = 7;
	public static final int UNUSAL_VOLUME = 8;

	public static final int SHARE_RESULT_COMPARATOR = 20;
	public static final int DIVIDEND_RESULT_COMPARATOR = 21;

	public static final int COMPANY_NAME_COMPARATOR = 30;
	public static final int COMPANY_TICKER_COMPARATOR = 30;

	public static Comparator<?> getInstance(int type, Sortable sortable) {
		if (comparatorMap.containsKey(type)) {
			return comparatorMap.get(type);
		}
		if (comparator == null) {
			comparator = new StockComparator();
		}

		if (type == INTRADAY_TIME_CMP) {
			comparatorMap.put(type,
					comparator.new IntradayTimeComparator<IntradayShareData>());
		}
		if (type == INTRADAY_SECTOR_TIME_CMP) {
			comparatorMap
					.put(type,
							comparator.new IntradaySectorTimeComparator<IntradaySectorData>());
		} else if (type == TOP_GAINER) {
			comparatorMap
					.put(type,
							comparator.new IntradayTopGainerComparator<IntradayShareData>());
		} else if (type == TOP_LOOSER) {
			comparatorMap
					.put(type,
							comparator.new IntradayTopLooserComparator<IntradayShareData>());
		} else if (type == HIGHEST_TURNOVER) {
			comparatorMap
					.put(type,
							comparator.new IntradayHighestTurnoverComparator<IntradayShareData>());
		} else if (type == MOST_ACTIVE) {
			comparatorMap
					.put(type,
							comparator.new IntradayMostActiveComparator<IntradayShareData>());
		} else if (type == MOST_VOLATALITY) {
			comparatorMap
					.put(type,
							comparator.new IntradayMostVolatalityComparator<IntradayShareData>());
		} else if (type == SHARE_RESULT_COMPARATOR) {
			comparatorMap.put(type,
					comparator.new ShareResultComparator<Result>(sortable));
		} else if (type == DIVIDEND_RESULT_COMPARATOR) {
			comparatorMap.put(type,
					comparator.new ShareResultComparator<Result>(sortable));
		}

		return comparatorMap.get(type);

	}

	public class ShareResultComparator<Result> implements Comparator<Result> {

		private Sortable sortable;

		public ShareResultComparator(Sortable sortable) {
			this.sortable = sortable;
		}

		public ShareResultComparator() {
		}

		public void setSortable(Sortable sortable) {
			this.sortable = sortable;
		}

		@Override
		public int compare(Result o1, Result o2) {

			String getMethod = "get"
					+ StringUtil.capitalizeFirst(sortable.getSortField());

			try {
				Method method1 = o1.getClass().getMethod(getMethod);
				Object obj = method1.invoke(o1);

				if (obj instanceof String) {
					String value1 = (String) obj;
					Method method2 = o2.getClass().getMethod(getMethod);
					String value2 = (String) method2.invoke(o2);

					if (sortable.getSortOrder().equals(Sortable.SORT_ORDER_ACS)) {
						return value1.compareTo(value2);
					} else {
						return -(value1.compareTo(value2));
					}
				} else {
					double value1 = (Double) obj;

					Method method2 = o2.getClass().getMethod(getMethod);
					double value2 = (Double) method2.invoke(o2);

					if (sortable.getSortOrder().equals(Sortable.SORT_ORDER_ACS)) {
						if (value1 > value2) {
							return 1;
						} else if (value1 < value2) {
							return -1;
						} else {
							return 0;
						}
					} else {
						if (value1 > value2) {
							return -1;
						} else if (value1 < value2) {
							return 1;
						} else {
							return 0;
						}
					}
				}
			} catch (Exception e) {
				logger.error(e.getCause());
			}
			return 0;
		}
	}

	public class IntradayTimeComparator<T extends IntradayShareData> implements
			java.util.Comparator<T> {
		@Override
		public int compare(T o1, T o2) {
			if (o1 == null || o2 == null)
				return 0;

			if (o1.getTime().after(o2.getTime())) {
				return -1;
			} else {
				return 1;
			}

		}
	}

	public class IntradaySectorTimeComparator<T extends IntradaySectorData>
			implements java.util.Comparator<T> {
		@Override
		public int compare(T o1, T o2) {
			if (o1 == null || o2 == null)
				return 0;

			if (o1.getDateTime().after(o2.getDateTime())) {
				return -1;
			} else {
				return 1;
			}

		}
	}

	public class IntradayTopGainerComparator<T extends IntradayShareData>
			implements java.util.Comparator<T> {
		@Override
		public int compare(T o1, T o2) {
			if (o1 == null || o2 == null)
				return 0;

			if (o1.getPercentageChange() > (o2.getPercentageChange())) {
				return -1;
			} else {
				return 1;
			}

		}
	}

	public class IntradayTopLooserComparator<T extends IntradayShareData>
			implements java.util.Comparator<T> {
		@Override
		public int compare(T o1, T o2) {
			if (o1 == null || o2 == null)
				return 0;

			if (o1.getPercentageChange() > (o2.getPercentageChange())) {
				return 1;
			} else {
				return -1;
			}

		}
	}

	public class IntradayHighestTurnoverComparator<T extends IntradayShareData>
			implements java.util.Comparator<T> {
		@Override
		public int compare(T o1, T o2) {
			if (o1 == null || o2 == null)
				return 0;

			if (o1.getTurnover() < (o2.getTurnover())) {
				return 1;
			} else {
				return -1;
			}

		}
	}

	public class IntradayMostActiveComparator<T extends IntradayShareData>
			implements java.util.Comparator<T> {
		@Override
		public int compare(T o1, T o2) {
			if (o1 == null || o2 == null)
				return 0;

			if (o1.getShareVolume() < (o2.getShareVolume())) {
				return 1;
			} else {
				return -1;
			}

		}
	}

	public class IntradayMostVolatalityComparator<T extends IntradayShareData>
			implements java.util.Comparator<T> {
		@Override
		public int compare(T o1, T o2) {
			if (o1 == null || o2 == null)
				return 0;

			if ((o1.getHigh() / o1.getLow() - 1) < (o2.getHigh() / o2.getLow() - 1)) {
				return 1;
			} else {
				return -1;
			}

		}
	}

}
